#include <iostream>

#include "log.h"
#include "util.h"
#include "config.h"
#include <yaml-cpp/node/node.h>
#include <yaml-cpp/yaml.h>
#include <vector>
#include <list>
#include <set>


using namespace std;

//johnsonli::ConfigVar<float>::ptr g_int_valuex_config =
//	johnsonli::Config::Lookup<float>("system.port", 8080.888, "system port");

johnsonli::ConfigVar<int>::ptr g_int_value_config =
	johnsonli::Config::Lookup<int>("system.port", 8080, "system port");

johnsonli::ConfigVar<float>::ptr g_float_value_config =
	johnsonli::Config::Lookup<float>("system.value", 10.9999, "system value");

johnsonli::ConfigVar<std::vector<int>>::ptr g_int_vec_value_config =
	johnsonli::Config::Lookup<vector<int>>("system.int_vec", std::vector<int>{1,2}, "system int vec");

johnsonli::ConfigVar<std::list<int>>::ptr g_int_lst_value_config =
	johnsonli::Config::Lookup<std::list<int>>("system.int_lst", std::list<int>{3, 4}, "system int lst");

johnsonli::ConfigVar<std::set<int>>::ptr g_int_set_value_config =
	johnsonli::Config::Lookup<std::set<int>>("system.int_set", std::set<int>{5, 6}, "system int set");

johnsonli::ConfigVar<std::unordered_set<int>>::ptr g_int_uset_value_config =
	johnsonli::Config::Lookup<std::unordered_set<int>>("system.int_uset", std::unordered_set<int>{5, 6, 1, 4}, "system int uset");

johnsonli::ConfigVar<std::map<std::string, int>>::ptr g_str_int_map_value_config =
	johnsonli::Config::Lookup<std::map<std::string, int>>("system.str_int_map", std::map<std::string, int>{ {"key", 200}}, "system str int map");

johnsonli::ConfigVar<std::unordered_map<std::string, int>>::ptr g_str_int_umap_value_config =
	johnsonli::Config::Lookup<std::unordered_map<std::string, int>>("system.str_int_umap", std::unordered_map<std::string, int>{ {"key", 200}}, "system str int umap");


void test_yaml()
{
	YAML::Node root = YAML::LoadFile("/home/ubuntu/project/c++/ServerFramework/yaml.txt");
	LOG_INFO(LOG_ROOT()) << root;
}

void test_config()
{
	LOG_INFO(LOG_ROOT()) << "before: " << g_int_value_config->getValue();
	LOG_INFO(LOG_ROOT()) << "before: " << g_float_value_config->toString();

#define XX(g_var, name, prefix) \
	{ \
		auto v = g_var->getValue(); \
		for (auto i : v) \
		{ \
			LOG_INFO(LOG_ROOT()) << #prefix" "#name": " << i; \
		} \
		LOG_INFO(LOG_ROOT()) << #prefix" "#name" yaml: " << g_var->toString(); \
	} 
#define XX_M(g_var, name, prefix) \
	{ \
		auto v = g_var->getValue(); \
		for (auto i : v) \
		{ \
			LOG_INFO(LOG_ROOT()) << #prefix" "#name": {" \
				<< i.first << "-" << i.second << "}"; \
		} \
		LOG_INFO(LOG_ROOT()) << #prefix" "#name" yaml: " << g_var->toString(); \
	}

	XX(g_int_vec_value_config, int_vec, before);
	XX(g_int_lst_value_config, int_lst, before);
	XX(g_int_set_value_config, int_set, before);
	XX(g_int_uset_value_config, int_uset, before);
	XX_M(g_str_int_map_value_config, str_int_map, before);
	XX_M(g_str_int_umap_value_config, str_int_umap, before);
	
	YAML::Node root = YAML::LoadFile("/home/ubuntu/project/c++/ServerFramework/yaml.txt");
	johnsonli::Config::LoadFromYaml(root);

	LOG_INFO(LOG_ROOT()) << "after: " << g_int_value_config->getValue();
	LOG_INFO(LOG_ROOT()) << "after: " << g_float_value_config->toString();

	XX(g_int_vec_value_config, int_vec, after);
	XX(g_int_lst_value_config, int_lst, after);
	XX(g_int_set_value_config, int_set, after);
	XX(g_int_uset_value_config, int_set, after);
	XX_M(g_str_int_map_value_config, str_int_map, after);
	XX_M(g_str_int_umap_value_config, str_int_umap, after);
}


class Person
{
public:
	std::string m_name;
	int m_age = 0;
	bool m_sex = 0;

	std::string toString() const
	{
		std::stringstream ss;
		ss << "person{name: " << m_name << ", age: " << m_age << ", sex: " << m_sex << "}";
		return ss.str();
	}

	bool operator==(const Person& p) const
	{
		return m_name == p.m_name
			&& m_age == p.m_age
			&& m_sex == p.m_sex;
	}
		
};

namespace johnsonli
{
	template<>
	class LexicalCast<std::string, Person>
	{
	public:
		Person operator()(const std::string& str)
		{
			YAML::Node node = YAML::Load(str);
			Person p;
			p.m_name = node["name"].as<std::string>();
			p.m_age = node["age"].as<int>();
			p.m_sex = node["sex"].as<bool>();
			return p;
		}
	};

	template<>
	class LexicalCast<Person, std::string>
	{
	public:
		string operator()(const Person& p)
		{
			YAML::Node node;
			node["name"] = p.m_name;
			node["age"] = p.m_age;
			node["sex"] = p.m_sex;
			std::stringstream ss;
			ss << node;
			return ss.str();
		}
	};
}

johnsonli::ConfigVar<Person>::ptr g_person = johnsonli::Config::Lookup("class.person", Person(), "class person");

johnsonli::ConfigVar<std::map<std::string, Person>>::ptr g_person_map = johnsonli::Config::Lookup("class.map", std::map<std::string, Person>(), "class map");

johnsonli::ConfigVar<std::map<std::string, std::vector<Person>>>::ptr g_person_vec_map = johnsonli::Config::Lookup("class.vec_map", std::map<std::string, std::vector<Person>>(), "class vec_map");


void test_class()
{
	LOG_INFO(LOG_ROOT()) << "before: " << g_person_vec_map->toString();
	LOG_INFO(LOG_ROOT()) << "before: " << g_person->getValue().toString();

	g_person->addListener([](const Person& old_val, const Person& new_val) {
		LOG_INFO(LOG_ROOT()) << "old val = " << old_val.toString()
			<< " new val = " << new_val.toString();
	});
	
#define XX_PM(g_var, prefix) \
	{ \
		auto m = g_var->getValue(); \
		for(auto& i : m) \
		{ \
			LOG_INFO(LOG_ROOT()) << #prefix << ": " << i.first << "-" << i.second.toString(); \
		} \
	}
	XX_PM(g_person_map, "class.map before");

	YAML::Node root = YAML::LoadFile("/home/ubuntu/project/c++/ServerFramework/yaml.txt");
	johnsonli::Config::LoadFromYaml(root);
	LOG_INFO(LOG_ROOT()) << "after: " << g_person->getValue().toString();
	
	XX_PM(g_person_map, "class.map after");

	LOG_INFO(LOG_ROOT()) << "after: " << g_person_vec_map->toString();
}

void test_log()
{
	johnsonli::Logger::ptr system_log = LOG_NAME("system");
	std::cout << johnsonli::LoggerMgr::GetInstance()->toYamlString() << std::endl;
	
	LOG_DEBUG(system_log) << "hello system";

	YAML::Node root = YAML::LoadFile("/home/ubuntu/project/c++/ServerFramework/logs.yaml");
	johnsonli::Config::LoadFromYaml(root);

	std::cout << "=======================================" << std::endl;

	std::cout << johnsonli::LoggerMgr::GetInstance()->toYamlString() << std::endl;
	LOG_INFO(system_log) << "hello system";
}

int main()
{
	//测试Logger
	//auto logger = johnsonli::LoggerMgr::GetInstance()->getLogger("root");
	//johnsonli::FileLogAppender::ptr file_apd(new johnsonli::FileLogAppender("./log8.txt"));
	//file_apd->setLevel(johnsonli::LogLevel::ERROR);
	//logger->addAppender(file_apd);
	//
	//LOG_ERROR(logger) << "test error";
	//LOG_DEBUG(logger) << "test debug";
	//LOG_FMT_DEBUG(logger, "test %s", "root");

	//测试Config
	//test_config();
	//test_class();
	test_log();
	//johnsonli::ConfigVar<std::set<int>>::ptr g_v_i = johnsonli::Config::Lookup("vec_int", std::set<int>{1, 2, 3}, "vec int");
	//g_v_i->addListener(10, [](const set<int>& old_val, const set<int>& new_val) {
	//	LOG_INFO(LOG_ROOT()) << "on changed cbs";
	//});

	//YAML::Node root = YAML::LoadFile("/home/ubuntu/project/c++/ServerFramework/test.yaml");
	//johnsonli::Config::LoadFromYaml(root);

	getchar();

	return 0;
}